package j.framework.base.controller;

import j.data.ActionMessage;
import j.data.ActionResult;
import j.data.PagedData;
import j.data.tree.TreeData;
import j.framework.base.service.ApplicationService;
import j.framework.base.service.GeneralService;
import j.framework.core.*;
import j.framework.domain.BaseDomain;
import j.util.DES;
import j.util.SpringBeans;
import j.util.StringUtils;
import j.util.jdbc.JdbcTemplate;
import j.util.jdbc.TransactionalJdbcTemplate;
import j.util.web.ParamUtils;
import j.util.web.WebUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 统一的请求控制器，与表sys_web_request结合
 * User: jason
 * Date: 2014/11/10 15:25
 */
@WebServlet(urlPatterns = "/user/req")
public class WebRequestController extends HttpServlet {

    private MembershipProvider membershipProvider = FApplication.getMembershipProvider();

    private GeneralService generalService = SpringBeans.getBean(GeneralService.class);

    private ApplicationService applicationService = SpringBeans.getBean(ApplicationService.class);

    private JdbcTemplate jdbcTemplate = SpringBeans.getBean(JdbcTemplate.class);


    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestMethod = request.getMethod();
        boolean isPost = requestMethod.equals(WebRequestConfiguration.REQUEST_TYPE_POST);
        String reqId = DES.decrypt(request.getParameter("rid"));
        if(StringUtils.isNullOrEmpty(reqId)){
            processError(isPost,"无效的请求！",request,response);
            return;
        }
        WebRequestConfiguration wrc = FApplication.getInstance().getWebRequestConfiguration(reqId);
        if(wrc == null){
            processError(isPost,"无效的请求，请与系统开发人员联系！",request,response);
            return;
        }
        if(wrc.isUsedGeneralService() && wrc.getGeneralAction().equals(WebRequestConfiguration.GENERAL_ACTION_ALL_IN_ONE)){
            //此时，需要从前台传过来操作方式
            wrc.setGeneralAction(request.getParameter("action"));
        }else{
            //使用通用业务操作并且又是所有操作合在一起，则不再检测请求类型了
            if(wrc.getRequestType().equals(WebRequestConfiguration.REQUEST_TYPE_GET)
                    && requestMethod.equals(WebRequestConfiguration.REQUEST_TYPE_POST)){
                processError(isPost,"此请求仅支持Get请求！",request,response);
                return;
            }else if(wrc.getRequestType().equals(WebRequestConfiguration.REQUEST_TYPE_POST)
                    && requestMethod.equals(WebRequestConfiguration.REQUEST_TYPE_GET)){
                processError(isPost,"此请求仅支持POST请求！",request,response);
                return;
            }
        }
        if(wrc.isUsedGeneralService() && StringUtils.isNullOrEmpty(wrc.getGeneralAction())){
            //这是无效的请求
            processError(isPost,"无效的请求[此请求配置为使用系统通用类，但此请求未指定操作方式(edit|save|delete)]！",request,response);
            return;
        }
        LoginUser user = (LoginUser) WebUtils.getSessionAttribute(request, LoginUser.SESSION_KEY);
        //检测模块权限及操作权限
        SysLog log = SysLog.buildLog(user.getUserId(), wrc.getFunctionId(), wrc.getRequestName(), wrc.getGeneralAction(), null,WebUtils.getRequestIP(request));
        if(wrc.isCheckFunctionPrivilege()){
            if(!membershipProvider.hasFunction(user,wrc.getFunctionId())){
                //没有此模块权限
                processError(log,isPost,"没有模块["+wrc.getFunctionName()+"]的授权!",request,response);
                return;
            }
        }
        if(wrc.isCheckActionPrivilege()){
            if(!membershipProvider.hasAction(user, wrc.getFunctionId(), wrc.getActionId())){
                //没有此模块权限
                processError(log,isPost,"没有模块["+wrc.getFunctionName()+"]操作["+wrc.getActionName()+"]的授权!",request,response);
                return;
            }
        }
        //检测此请求是否仅转发页面
        if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_ONLY_FORWARD)){
            log.setLogContent("将请求转发到"+wrc.getUrl()+"页面");
            forward(request, response, wrc.getUrl());
            return;
        }
        //检测是否重定向到一个页面
        if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_REDIRECT)){
            log.setLogContent("将请求重定向到"+wrc.getUrl()+"页面");
            response.sendRedirect(wrc.getUrl());
            return;
        }
        //检测是否调用通用业务类
        ServiceContext context = new ServiceContext(user);
        loadRequestParams(request, context);
        log.setParamsInfo(context.params.toString());
        context.setLog(log);
        context.setLogSupported(wrc.isLogSupported());
        JdbcTemplate jt = new TransactionalJdbcTemplate(jdbcTemplate.getDataSource(),true);
        /*if(context.isLogSupported() || (wrc.isUsedGeneralService() &&
                (wrc.getGeneralAction().equals("save")||wrc.getGeneralAction().equals("delete")))){
            jt = new TransactionalJdbcTemplate(jdbcTemplate.getDataSource(),true);
        }else{
            jt = new QueryJdbcTemplate(jdbcTemplate.getDataSource());
        }*/
        context.setJdbcTemplate(jt);
        try {
            if(wrc.isUsedGeneralService()){
                //使用通用业务类
                EntityTrigger trigger = null;
                if(StringUtils.isNotEmpty(wrc.getEntityTrigger())){
                    try {
                        trigger = (EntityTrigger) Class.forName(wrc.getEntityTrigger()).newInstance();
                    }catch(Exception e){
                        processError(isPost,"初始化实体触发器时出错！",request, response);
                        return;
                    }
                }
                if(wrc.getGeneralAction().equals(WebRequestConfiguration.GENERAL_ACTION_EDIT)) {
                    Object entity = context.getBean(Class.forName(wrc.getEntityClassName()));
                    BaseDomain bd = (BaseDomain)entity;
                    if (StringUtils.isNotEmpty(bd.getId())) {
                        //这是修改一个实体类，调用通用业务层
                        if(trigger!=null){
                            trigger.updateForBefore(context, bd);
                        }
                        entity = generalService.findEntity(context,entity);
                    } else {
                        //这是增加
                        context.setJdbcTemplate(jdbcTemplate);
                        if(trigger!=null){
                            trigger.insertForBefore(context,bd);
                        }
                    }
                    request.setAttribute("d",entity);
                    //jt.commit();
                    forward(request, response, wrc.getUrl());
                }else if(wrc.getGeneralAction().equals(WebRequestConfiguration.GENERAL_ACTION_SAVE)){
                    //保存增加或修改
                    Object entity = context.getBean(Class.forName(wrc.getEntityClassName()));
                    if(context.actionResult.isOk()){
                        generalService.doSave(context,wrc.getEntityPkGenerateKey(),entity,trigger);
                        if(context.actionResult.isOk()){
                            applicationService.log(jt,log);
                            jt.commit();
                        }else{
                            jt.rollback();
                        }
                    }
                    //以iframe形式处理结果
                    toResult(log,context.actionResult, response);
                }else if(wrc.getGeneralAction().equals(WebRequestConfiguration.GENERAL_ACTION_DELETE)){
                    //删除实体
                    Object entity = context.getBean(Class.forName(wrc.getEntityClassName()));
                    if(context.actionResult.isOk()) {
                        generalService.doDelete(context, entity, trigger);
                        if(context.actionResult.isOk()) {
                            log.setOperateType("删除");
                            log.setLogContent("删除实体：" + entity.toString());
                            applicationService.log(jt, log);
                            jt.commit();
                        }else{
                            jt.rollback();
                        }
                    }

                    //以ajax处理结果
                    toAjaxClient(log,context.actionResult, response);
                }else{
                    processError(log,isPost,"此请求无效！",request,response);
                }
            }else{
                //不使用通用业务类，先找到业务bean，从spring托管容器中查找
                log.setBusinessType(wrc.getFunctionName());
                log.setOperateType(wrc.getRequestName());
                if(StringUtils.isNullOrEmpty(wrc.getBeanClassName())
                        || StringUtils.isNullOrEmpty(wrc.getBeanClassMethodName())){
                    processError(isPost,"业务bean及bean方法名参数缺失，请与开发人员联系！",request,response);
                    return;
                }
                Class<?> clazz;
                try {
                    clazz = Class.forName(wrc.getBeanClassName());
                }catch (ClassNotFoundException e){
                    processError(isPost,"业务bean["+wrc.getBeanClassName()+"]没有找到，请与开发人员联系！",request,response);
                    return;
                }
                Object bean = SpringBeans.getBean(clazz);
                Method method = clazz.getMethod(wrc.getBeanClassMethodName(),ServiceContext.class);
                if(method == null){
                    processError(isPost,"业务bean的方法["+wrc.getBeanClassMethodName()+"]不存在，请与开发人员联系！",request,response);
                    return;
                }
                //调用业务层方法
                Object result = method.invoke(bean,context);
                log.setClassName(wrc.getBeanClassName());
                log.setMethodName(wrc.getBeanClassMethodName());
                log.setReturnValue(String.valueOf(result));
                if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_FORWARD)){
                    request.setAttribute("result",result);
                    request.setAttribute(ActionResult.REQUEST_ITEMS_ATTRIBUTE_NAME,context.actionResult);
                    if(wrc.isLogSupported()) {
                        applicationService.log(jt, log);
                    }
                    jt.commit();
                    forward(request, response, wrc.getUrl());
                }else if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_FORM_IFRAME)){
                    if(wrc.isLogSupported()) {
                        applicationService.log(jt, log);
                    }
                    jt.commit();
                    toOut("<script>parent.doOk();</script>",response);
                }else if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_JSON)){
                    String json = j.util.json.JsonUtils.toJson(result);
                    if(wrc.isLogSupported()) {
                        applicationService.log(jt, log);
                    }
                    jt.commit();
                    toAjaxClient(context.actionResult, json, response);
                }else if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_AJAX)){
                    String json = j.util.json.JsonUtils.toJson(result);
                    if(wrc.isLogSupported()) {
                        applicationService.log(jt, log);
                    }
                    jt.commit();
                    toOut(json,response);
                }else if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_TREE)){
                    if(result instanceof TreeData){
                        if(wrc.isLogSupported()){
                            applicationService.log(jt,log);
                        }
                        jt.commit();
                        toOut(result.toString(), response);
                    } else {
                        processError(isPost,"业务bean的方法["+wrc.getBeanClassMethodName()+"]返回类型不是树结构，请与开发人员联系！",request,response);
                    }
                }else if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_DATAGRID)){
                        if(result instanceof List<?>){
                            ///不分页结构
                            List<?> list = (List<?>)result;
                            log.setReturnValue(j.util.json.JsonUtils.toJson(list));
                            if(StringUtils.isNullOrEmpty(log.getLogContent())){
                                log.setLogContent("不分页获取数据");
                            }
                            if(wrc.isLogSupported()) {
                                applicationService.log(jt, log);
                            }
                            jt.commit();
                            toOut(String.format("{\"total\":%s,\"rows\":%s}", list.size(), j.util.json.JsonUtils.toJson(list)), response);
                        }else if(result instanceof PagedData<?>){
                            //分页结构
                            PagedData<?> pd = (PagedData<?>)result;
                            log.setReturnValue(j.util.json.JsonUtils.toJson(pd.getDataItems()));
                            if(StringUtils.isNullOrEmpty(log.getLogContent())){
                                log.setLogContent("不分页获取数据");
                            }
                            if(wrc.isLogSupported()) {
                                applicationService.log(jt, log);
                            }
                            jt.commit();
                            toOut(String.format("{\"total\":%s,\"rows\":%s}",pd.getTotal(), j.util.json.JsonUtils.toJson(pd.getDataItems())),response);
                        }else{
                            //错误的类型
                            processError(log,isPost,"业务bean的方法["+wrc.getBeanClassMethodName()+"]返回类型不是树表结构，请与开发人员联系！",request,response);
                        }
                    }else if(wrc.getResponseType().equals(WebRequestConfiguration.RESPONSE_TYPE_DEFAULT)){
                        if(wrc.isLogSupported()) {
                            applicationService.log(jt, log);
                        }
                        jt.commit();
                        toOut(String.valueOf(result),response);
                    }else{
                        jt.commit();
                        toAjaxClient(context.actionResult,String.valueOf(result),response);
                    }
                }
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
            jt.rollback();
            processError(isPost,"没有找到实体类["+wrc.getEntityClassName()+"]",request,response);
        }catch (NoSuchMethodException e){
            e.printStackTrace();
            jt.rollback();
            processError(isPost,"没有找到实体类["+wrc.getBeanClassName()+"]的方法["+wrc.getBeanClassMethodName()+"]",request,response);
        }catch (InvocationTargetException e){
            e.printStackTrace();
            jt.rollback();
            Throwable t = e.getTargetException();
            String msg;
            if(t!=null){
                msg = t.getMessage();
            }else{
                msg = "调用"+wrc.getBeanClassName()+"."+wrc.getBeanClassMethodName()+"时出错！";
            }
            processError(isPost,msg,request,response);
        }catch (Exception e){
            jt.rollback();
            e.printStackTrace();
            processError(isPost,"调用系统服务时出错，异常："+e.getMessage(),request,response);
        }finally {
            jt.close();
        }
    }

    private void processError(boolean isPost,String msg,HttpServletRequest request,HttpServletResponse response)
            throws ServletException,IOException{
        processError(null,isPost,msg,request,response);
    }

    private void processError(SysLog log,boolean isPost,String msg,HttpServletRequest request,HttpServletResponse response)
        throws ServletException,IOException{
        String ajaxHeader = request.getHeader("HTTP_AJAX_POST_HEADER");
        boolean ajax = StringUtils.isNotEmpty(ajaxHeader);
        if(!ajax){
            int postByForm = ParamUtils.getIntParameter(request,"HTTP_POST_BY_FORM",1);
            if(postByForm == 2){
                ajax = true;
            }
        }
        ActionResult ar = ActionResult.New();
        ar.add(msg);
        if(null!=log){
            log.setException(msg);
            applicationService.log(log);
        }
        if(ajax){
            //这是ajax提交
            toAjaxClient(ar,response);
            return;
        }
        //看是否是iframe提交
        boolean isForm = ParamUtils.getBooleanParameter(request, "HTTP_POST_BY_FORM");
        if(isForm){
            toResult(ar,response);
        }else{
            //直接将错误信息显示在页面上
            toOut(String.format("<div style='color:#f00;padding:10px;'>%s</div>",msg),response);
        }
    }

    public void toResult(ActionResult ar,HttpServletResponse response)throws IOException{
        toResult(null,ar,response);
    }

    public void toResult(SysLog log,ActionResult ar,HttpServletResponse response)throws IOException{
        String scripts;
        if(ar.hasErrors()){
            //有错误,生成错误列表
            List<ActionMessage> errs = ar.toList();
            StringBuilder s = new StringBuilder();
            int i = 1;
            for(ActionMessage e : errs){
                s.append(i).append(".").append(e.getMessage()).append("\\n");
            }
            if(null != log){
                log.setException(s.toString().replaceAll("\\s", ""));
                applicationService.log(log);
            }
            scripts = String.format("<script>parent.f.setError(\"%s\");</script>",s.toString().replaceAll("\\s",""));
        }else{
            scripts = String.format("<script>parent.f.close();</script>");
        }
        toOut(scripts, response);
    }
    public void toAjaxClient(ActionResult ar,HttpServletResponse response)throws IOException{
        toAjaxClient(null,ar,response);
    }
    public void toAjaxClient(SysLog log,ActionResult ar,HttpServletResponse response)throws IOException{
        String outS;
        if(ar.hasErrors()){
            //有错误,生成错误列表
            List<ActionMessage> errs = ar.toList();
            StringBuilder s = new StringBuilder();
            int i = 1;
            for(ActionMessage e : errs){
                s.append(i).append(".").append(e.getMessage());
            }
            if(null!=log){
                log.setException(s.toString());
                applicationService.log(log);
            }
            response.setStatus(500);
            outS = String.format("{hasErrors:true,message:'%s'}",s.toString().replaceAll("'","\\\\'"));
        }else{
            outS = String.format("{%s:false}","hasErrors");
        }
        toOut(outS,response);
    }
    public void toAjaxClient(ActionResult ar,String data,HttpServletResponse response)throws IOException{
        toAjaxClient(null,ar,data,response);
    }
    public void toAjaxClient(SysLog log,ActionResult ar,String data,HttpServletResponse response)throws IOException{
        String outS;
        if(ar.hasErrors()){
            //有错误,生成错误列表
            List<ActionMessage> errs = ar.toList();
            StringBuilder s = new StringBuilder();
            int i = 1;
            for(ActionMessage e : errs){
                s.append(i).append(".").append(e.getMessage());
            }
            if(null!=log){
                applicationService.log(log);
            }
            outS = String.format("{hasErrors:true,message:'%s'}",s.toString());
        }else{
            if(StringUtils.isNotEmpty(data) && (data.startsWith("{") || data.startsWith("["))){
                outS = String.format("{%s:false,data:%s}","hasErrors",data);
            }else if(StringUtils.isNotEmpty(data)){
                outS = String.format("{%s:false,data:'%s'}","hasErrors",data.replaceAll("'","\\\\'"));
            }else{
                outS = String.format("{%s:false}","hasErrors");
            }
        }
        toOut(outS,response);
    }

    public void toOut(String s,HttpServletResponse response) throws IOException{
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print(s);
        out.flush();
    }

    private void forward(HttpServletRequest request,HttpServletResponse response,String jspPage) throws ServletException,IOException{
        String s = "/WEB-INF/jsp/" + jspPage + ".jsp";
        request.getRequestDispatcher(s).forward(request,response);
    }

    public static void loadRequestParams(HttpServletRequest request,ServiceContext context){
        Map<String,String[]> _m = request.getParameterMap();
        for(Map.Entry<String,String[]> entry : _m.entrySet()){
            if(entry.getValue().length==1){
                context.params.put(entry.getKey(), entry.getValue()[0]);
            }else{
                context.params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
            }
        }
    }
}
